﻿using Google.Protobuf.WellKnownTypes;
using log4net;
using log4net.Config;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using PhysicalExamination5.Service;
using SqlSugar;
using System;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace PhysicalExamination5.WebApi.Utility.Filters
{
    /// <summary>
    /// 拦截器 记录请求API入参和出参数
    /// </summary>
    public class CustomActionFilterAttribute : ActionFilterAttribute
    {

        private static readonly ILog log = LogManager.GetLogger(typeof(CustomActionFilterAttribute));
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly ISqlSugarClient db;
        static IConfiguration _configuration;

        /// <summary>
        /// 拦截器无参构造函数
        /// </summary>
        /// <param name="logger"></param>
        public CustomActionFilterAttribute(IHttpContextAccessor httpContextAccessor, IConfiguration IConfiguration)
        {
            _httpContextAccessor = httpContextAccessor;
            _configuration = IConfiguration;
        }

        /// <summary>
        /// 在执行xxxAPI接口执行之后
        /// </summary>
        /// <param name="context"></param>
        /// <exception cref="NotImplementedException"></exception>
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Exception != null)
            {
                //校验日志是否处理 防止出现二次日志几率
                if (context.ExceptionHandled == false) //如果没有被处理
                {
                    // 记录异常日志
                    if (log != null)
                    {
                        log.Error("错误信息:" + context.Exception.Message);
                        //标注已经处理过该异常了
                        context.ExceptionHandled = true;
                    }
                }
            }

            // 记录请求结果日志
            ControllerActionDescriptor desc = context.ActionDescriptor as ControllerActionDescriptor;
            if (desc != null)
            {
                ObjectResult rst = context.Result as ObjectResult;
                object rstValue = rst != null ? rst.Value : null;
                var logText = CreateResponseLogText(
                    context.HttpContext.Request.Method,
                    desc.ControllerName,
                    desc.ActionName,
                    rstValue);
                //记录日志
                log.Debug(logText);
            }
        }

        /// <summary>
        /// 在执行xxxAPI接口执行之前
        /// </summary>
        /// <param name="context"></param>
        /// <exception cref="NotImplementedException"></exception>
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            // 记录请求参数日志
            ControllerActionDescriptor desc = context.ActionDescriptor as ControllerActionDescriptor;
            if (desc != null)
            {
                var logText = CreateRequestLogText(
                    context.HttpContext.Request.Method,
                    desc.ControllerName,
                    desc.ActionName,
                    context.ActionArguments);
                //记录日志
                log.Debug(logText);
            }



            //获取配置JSON文件中的字段判断是正式环境还是测试环境
            string strShiFouZhengShi = _configuration["ShiFouZhengShi"];
            if (string.IsNullOrEmpty(strShiFouZhengShi))
            {
                strShiFouZhengShi = "是";
            }

            if (strShiFouZhengShi == "是")
            {
                //获取Head头中的CheckKey值
                string AesJiaMiZhi = context.HttpContext.Request.Headers["CheckKey"];
                if (string.IsNullOrEmpty(AesJiaMiZhi))
                {
                    //错误加密校验值为空!
                    log.Debug("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]请求参数不合法,触发拦截请求!,校验值为空!");
                    context.Result = new BadRequestObjectResult("请求参数不合法,触发拦截请求!");
                    return;
                }
                else
                {
                    string strQingQiuShiJianChuo = new MethodHelper(db).AesDecrypt(AesJiaMiZhi);
                    if (string.IsNullOrEmpty(strQingQiuShiJianChuo))
                    {
                        log.Debug("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]请求参数不合法,触发拦截请求!,校验值加密不正确!");
                        context.Result = new BadRequestObjectResult("请求参数不合法,触发拦截请求!");
                        return;
                    }
                    long logQingQiuTime = long.Parse(strQingQiuShiJianChuo);

                    DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
                    long logDangQianTime = (long)(DateTime.Now - startTime).TotalSeconds; // 相差秒数

                    DateTime beginTime = DateTime.UnixEpoch.AddSeconds(logQingQiuTime); // 将开始时间戳转换为DateTime类型
                    DateTime endTime = DateTime.UnixEpoch.AddSeconds(logDangQianTime); // 将结束时间戳转换为DateTime类型

                    log.Debug("请求时间[" + beginTime + "],服务器时间[" + endTime + "]");

                    TimeSpan timeDiff = endTime.Subtract(beginTime); // 计算时间差

                    double secondsDiff = timeDiff.TotalSeconds; // 获取时间差的总秒数

                    log.Debug("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]请求间隔秒数:" + secondsDiff + "");

                    if (secondsDiff > 5)
                    {
                        log.Debug("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]请求已超时,间隔秒数:" + secondsDiff + ",触发拦截请求!");
                        context.Result = new BadRequestObjectResult("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]请求已超时,间隔秒数:" + secondsDiff + ",触发拦截请求!");
                        return;
                    }
                }

                //过滤请求中的敏感词
                var request = context.HttpContext.Request;
                if (request.Method == "GET")
                {
                    var query = request.Query;
                    foreach (var key in query.Keys)
                    {
                        var value = query[key];
                        if (IsSqlInjection(value))
                        {
                            log.Debug("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]请求触发敏感词拦截!");
                            context.Result = new BadRequestObjectResult("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]请求触发敏感词拦截!");
                            return;
                        }
                    }
                    log.Debug("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]过滤敏感词通过!");
                }
                else if (request.Method == "POST")
                {
                    var ListAction = context.ActionArguments;

                    if (ListAction.Count > 0)
                    {
                        foreach (var p in ListAction)
                        {
                            if (p.Value.ToString().Contains("FormFile") == false)
                            {
                                //校验关键值
                                if (IsSqlInjection(p.Value.ToString()))
                                {
                                    log.Debug("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]请求触发敏感词拦截!");
                                    context.Result = new BadRequestObjectResult("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]请求触发敏感词拦截!");
                                    return;
                                }
                            }
                        }
                        log.Debug("当前[" + desc.ActionName + "],[" + desc.ControllerName + "]过滤敏感词通过!");
                    }
                }
            }
        }


        /// <summary>
        /// 校验是否存在sql注入风险
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private bool IsSqlInjection(string value)
        {
            bool bolShiFou = false;
            //构造SQL的注入关键字符
            string[] strBadChar = {
                "and"
                ,"exec"
                ,"insert"
                ,"select"
                ,"delete"
                ,"update"
                ,"count"
                ,"from"
                ,"drop"
                ,"asc"
                ,"char"
                ,"or"
                ,"*"
                ,"0x"
                ,">"
                ,"<"
                ,"="
                ,"|"
                ,","
                ,"#"
                //,"/"
                ,"//"
                ,"%"
                ,"--"
                ,"all"
                ,"union"
                ,"chr"
                ,"mid"
                ,"master"
                ,"truncate"
                ,"char"
                ,"declare"
                ,"SiteName"
                ,"net user"
                ,"xp_cmdshell"
                ,"/add"
                ,"exec master.dbo.xp_cmdshell"
                ,"net localgroup administrators"};

            //循环校验
            for (int i = 0; i < strBadChar.Length - 1; i++)
            {
                if (value.Contains(strBadChar[i]))
                {
                    bolShiFou = true;
                }
            }

            return bolShiFou;
        }

        /// <summary>
        /// 创建请求日志文本
        /// </summary>
        /// <param name="method">请求方法</param>
        /// <param name="controllerName">控制器名称</param>
        /// <param name="actionName">方法名称</param>
        /// <param name="actionArgs">方法参数</param>
        /// <returns></returns>
        private static string CreateRequestLogText(string method, string controllerName, string actionName, IDictionary<string, object> actionArgs)
        {
            StringBuilder strLog = new StringBuilder();
            strLog.AppendLine($"收到请求[{method}]/{controllerName}/{actionName}，参数：");
            if (actionArgs.Count > 0)
            {
                foreach (var p in actionArgs)
                {
                    strLog.AppendLine($"    " + p.Key + "：" + Newtonsoft.Json.JsonConvert.SerializeObject(p.Value));
                }
            }
            else
            {
                strLog.AppendLine("    无");
            }
            return strLog.ToString();
        }


        // <summary>
        /// 创建响应日志文本
        /// </summary>
        /// <param name="method">请求方法</param>
        /// <param name="controllerName">控制器名称</param>
        /// <param name="actionName">方法名称</param>
        /// <param name="result">执行结果</param>
        /// <returns></returns>
        private static string CreateResponseLogText(string method, string controllerName, string actionName, object result)
        {
            StringBuilder strLog = new StringBuilder();
            strLog.AppendLine($"完成请求[{method}]/{controllerName}/{actionName}，结果：");
            if (result != null)
            {
                strLog.AppendLine("    " + Newtonsoft.Json.JsonConvert.SerializeObject(result));
            }
            else
            {
                strLog.AppendLine("    无");
            }
            return strLog.ToString();
        }

    }
}
